package cachex

import (
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/mapx"
	"gitee.com/zhongguo168a/gocodes/datax/schemax"
	"gitee.com/zhongguo168a/gocodes/datax/schemax/basickind"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"sync"
)

func SetClassCreator(class string, creator func(data datax.M) IObject) {
	if creatorDict == nil {
		creatorDict = map[string]func(data datax.M) IObject{}
	}
	creatorDict[class] = creator
}

func GetClassCreator(key string) func(data datax.M) IObject {
	mutex.RLock()
	creator := creatorDict[key]
	mutex.RUnlock()

	return creator
}

//
var creatorDict map[string]func(data datax.M) IObject
var mutex sync.RWMutex

func NewObject(data datax.M) (obj *Object) {
	return NewObjectByType(
		&schemax.MapType{
			Key: &schemax.BasicType{
				Kind: basickind.String,
			},
			Value: &schemax.AnyType{}},
		data)
}

// 需要提前加载schemax的类型描述数据，否则报告异常
func NewObjectByType(schemaType schemax.IType, data datax.M) (obj *Object) {
	if schemaType == nil {
		panic("schemaType is nil")
	}
	obj = newObjectByType(schemaType)
	obj.refUpdate(data)
	return obj
}

func newObjectByType(schemaType schemax.IType) (obj *Object) {
	obj = &Object{}
	obj.schemaType = schemaType
	switch otype := schemaType.(type) {
	case *schemax.ClassType:
		obj.schemaDecl = schemax.GetDeclByKey(otype.Decl).(*schemax.ClassDecl)
	}
	obj.this = obj
	return obj
}

type Object struct {
	this       IObject
	schemaType schemax.IType
	// 当schemaType为class时,临时保存
	schemaDecl *schemax.ClassDecl
	classId    string
	// 当schemaType为map/array/anyObject时的类型
	anyType string

	// 当前管理的数据
	data        datax.M
	itemCreator func(data datax.M) IObject
	versionTime map[string]interface{}
	versionUsed bool
	// 1-class 使用field
	// 2-array/map 使用modifyMapOrArray
	modifyUsed           bool
	modifyMode           int
	modifyField          map[string]IModify
	modifyMapOrArray     IModify
	modifyMapOrArrayItem interface{}
	modifyContext        interface{}
	//
	mutex sync.RWMutex
}

func (s *Object) RefClassId() string {
	return s.classId
}
func (s *Object) RefClassType() string {
	if s.schemaDecl == nil {
		return ""
	}
	return s.schemaDecl.Name
}
func (s *Object) RefClassCatalog() string {
	if s.schemaDecl == nil {
		return ""
	}
	return s.schemaDecl.Tags["config"]
}

func (s *Object) RefUseVersion() {
	s.versionUsed = true
	s.versionTime = map[string]interface{}{}
}
func (s *Object) RefSetThis(val IObject) {
	s.this = val
}
func (s *Object) RefSetContext(modifyContext interface{}) {
	s.modifyContext = modifyContext
	for _, fieldVal := range s.data {
		switch fval := fieldVal.(type) {
		case IObject:
			fval.RefSetContext(modifyContext)
		}
	}
}
func (s *Object) RefSetClassId(classId string) {
	s.classId = classId
}

func (s *Object) RefUseModify() {
	s.modifyUsed = true
	switch s.schemaType.(type) {
	case *schemax.MapType:
		s.modifyMode = 2
	case *schemax.ArrayType:
		s.modifyMode = 2
	case *schemax.ClassType:
		s.modifyMode = 1
	}
}
func (s *Object) RefSetMapOrArrayModify(modify IModify) {
	if !s.modifyUsed {
		s.RefUseModify()
	}
	s.modifyMapOrArray = modify
	data0, ok := s.data["0"]
	if ok {
		s.modifyMapOrArrayItem = data0
		switch data0.(type) {
		case IObject:
			s.modifyMapOrArrayItem = data0.(IObject).toMap()
		default:
			s.modifyMapOrArrayItem = data0
		}
	}

}

func (s *Object) RefSetClassModify(field string, modify IModify) {
	if !s.modifyUsed {
		s.RefUseModify()
	}
	modifyField := s.modifyField
	if modifyField == nil {
		modifyField = map[string]IModify{}
		s.modifyField = modifyField
	}
	modifyField[field] = modify
}

// 新建对象的数据, 旧的数据清除
func (s *Object) RefNew() IObject {
	s.mutex.Lock()
	s.refNew()
	s.mutex.Unlock()
	return s.this
}

func (s *Object) refNew() {
	s.data = datax.M{}
}

// 如果是空的才新建对象的数据
// 返回自身，便于链式调用
func (s *Object) RefNewIfNil() IObject {
	if s.data == nil {
		return s.RefNew()
	}
	return s.this
}

func (s *Object) RefIsNil() bool {
	return s.data == nil
}

func (s *Object) creaetSchemaClass(classType *schemax.ClassType, fieldVal interface{}) IObject {
	creator := GetClassCreator(classType.Decl)
	if creator == nil {
		panic(errorx.New("class type must set creator"))
	}
	var fieldObj IObject
	if fieldVal == nil {
		fieldObj = creator(nil)
	} else {
		fieldObj = creator(fieldVal.(datax.M))
	}
	fieldObj.RefSetThis(fieldObj)
	return fieldObj
}

func (s *Object) createSchemaAny(fieldVal interface{}, tagType string) (result interface{}, err error) {
	var fieldObj IObject
	switch fval := fieldVal.(type) {
	case datax.M:
		if fval != nil {
			_type := mapx.String(fval, "_type")
			if _type != "" {
				_typeDecl := schemax.GetDeclByKey(_type).(*schemax.ClassDecl)
				if _typeDecl == nil {
					return nil, errorx.New("any type decl not found", datax.M{"_type": _type})
				}
				creator := GetClassCreator(_typeDecl.GetIdent())
				if creator == nil {
					return nil, errorx.New("any type creator not found", datax.M{"_type": _type})
				}

				fieldObj = creator(fieldVal.(datax.M))
				fieldObj.RefSetThis(fieldObj)
				fieldObj.RefSetAnyType(tagType)
				fieldObj.refUpdate(fval)
				return fieldObj, nil
			}
		}
		fieldObj = NewObject(nil)
		fieldObj.RefSetThis(fieldObj)
		fieldObj.RefSetAnyType(tagType)
		fieldObj.refUpdate(fval)
		return fieldObj, nil
	default:
	}

	return fieldVal, nil
}

func (s *Object) RefLength() int {
	if s.modifyMode == 2 {
		if s.modifyMapOrArray != nil {
			return s.modifyMapOrArray.Length()
		}
		return 0
	}
	return len(s.data)
}

func (s *Object) RefKeys() (r []string) {
	s.mutex.RLock()
	for key := range s.data {
		r = append(r, key)
	}
	s.mutex.RUnlock()
	return
}

func (s *Object) RefNewByMap(data map[string]interface{}) {
	s.mutex.Lock()
	s.refSetNil()
	s.refUpdate(data)
	s.mutex.Unlock()
	return
}
